/*
从2025年1月1日起，
- 男职工 和 原法定退休年龄为五十五周岁的女职工，
    - 法定退休年龄每四个月延迟一个月，分别逐步延迟至六十三周岁和五十八周岁；
- 原法定退休年龄为五十周岁的女职工，
    - 法定退休年龄每二个月延迟一个月，逐步延迟至五十五周岁。
*/

use std::collections::HashMap;
use std::ops::{Add, Sub};

pub fn retire_time(time: &str, tp: &str) -> String {
    let birthday = Date::from_str(time);
    let ty = PersonnelType::from_str(tp);
    let delay = ty.delay();
    let policy = Policy::new(delay);

    // three cases:
    // 1. retire before policy starts
    // 2. retire during policy
    // 3. retire after policy ends
    if policy.start_date - birthday > delay.start_age {
        // println!("Retire before policy starts");
        let retire_date = birthday + delay.start_age;
        format!("{},{},{}", retire_date, delay.start_age / 12, 0)
    } else if policy.end_date - birthday <= delay.end_age {
        // println!("retire after policy ends");
        let retire_date = birthday + delay.end_age;
        format!(
            "{},{},{}",
            retire_date,
            delay.end_age / 12,
            delay.end_age - delay.start_age
        )
    } else {
        // println!("retire during policy");
        let mut age_date = birthday;
        loop {
            age_date = age_date + 1;
            let age_month = age_date - birthday;
            if age_date < policy.start_date {
                continue;
            }
            let retire_months = match policy.months_when_retired.get(&age_date) {
                Some(&months) => months,
                None => continue,
            };
            if age_month < retire_months {
                continue;
            }
            println!("{}, {}, {}", age_date, age_month, retire_months);
            let adjusted_retire_months = if age_month == delay.start_age {
                retire_months + 1
            } else {
                retire_months
            };
            if age_month == delay.start_age {
                age_date = age_date + 1;
            }
            return format!(
                "{},{:.2},{}",
                age_date,
                adjusted_retire_months as f64 / 12.0,
                adjusted_retire_months - delay.start_age
            );
        }
    }
}

#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub struct Date {
    year: isize,
    month: isize,
}

impl std::fmt::Display for Date {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}-{:02}", self.year, self.month)
    }
}

impl Date {
    pub fn from_str(s: &str) -> Self {
        let mut iter = s.split('-');
        let year = iter.next().unwrap().parse::<isize>().unwrap();
        let month = iter.next().unwrap().parse::<isize>().unwrap();
        Self { year, month }
    }
}

impl PartialOrd for Date {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Date {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        if self.year != other.year {
            self.year.cmp(&other.year)
        } else {
            self.month.cmp(&other.month)
        }
    }
}

impl Add<usize> for Date {
    type Output = Self;

    fn add(self, rhs: usize) -> Self::Output {
        let mut year = self.year;
        let mut month = self.month + rhs as isize;
        while month > 12 {
            year += 1;
            month -= 12;
        }
        Date { year, month }
    }
}

impl Sub<usize> for Date {
    type Output = Self;

    fn sub(self, rhs: usize) -> Self::Output {
        let mut year = self.year;
        let mut month = self.month - rhs as isize;
        while month < 1 {
            year -= 1;
            month += 12;
        }
        Date { year, month }
    }
}

impl Sub<Date> for Date {
    type Output = usize;

    fn sub(self, rhs: Date) -> Self::Output {
        let mut months = (self.year - rhs.year) * 12;
        months += self.month - rhs.month;
        months as usize
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Delay {
    start_age: usize,    // 起点年龄(以月为单位)
    period_month: usize, // 延迟间隔
    end_age: usize,      // 终点年龄(以月为单位)
}

impl Delay {
    pub fn new(start_age: usize, period_month: usize, end_age: usize) -> Self {
        let start_age = start_age * 12;
        let end_age = end_age * 12;
        Self {
            start_age,
            period_month,
            end_age,
        }
    }
}

#[derive(Debug)]
pub enum PersonnelType {
    Man(Delay),     // 60, 4, 63
    Woman55(Delay), // 55, 4, 58
    Woman50(Delay), // 50, 2, 55
}

impl PersonnelType {
    pub fn from_str(s: &str) -> Self {
        match s {
            "原法定退休年龄55周岁女职工" => Self::Woman55(Delay::new(55, 4, 58)),
            "原法定退休年龄50周岁女职工" => Self::Woman50(Delay::new(50, 2, 55)),
            "男职工" => Self::Man(Delay::new(60, 4, 63)),
            _ => panic!("Invalid personnel type"),
        }
    }

    pub fn delay(&self) -> Delay {
        match self {
            Self::Man(delay) => *delay,
            Self::Woman55(delay) => *delay,
            Self::Woman50(delay) => *delay,
        }
    }
}

#[derive(Debug)]
pub struct Policy {
    start_date: Date, // 2025-01
    end_date: Date,   // to be calculated
    // how many months a person should live at least when retired
    // key: the date when the person is retired
    // value: the months the person needs to live
    months_when_retired: HashMap<Date, usize>,
}

impl Policy {
    pub fn new(delay: Delay) -> Self {
        let start_date = Date {
            year: 2025,
            month: 1,
        };
        let duration = (delay.end_age - delay.start_age) * delay.period_month;
        let end_date = start_date + duration;
        let mut months_when_retired = HashMap::new();

        let mut ptr = start_date.clone();
        while ptr < end_date {
            let delta_months = ptr - start_date;
            let delayed_months = delta_months / delay.period_month;

            let months = delay.start_age + delayed_months;
            months_when_retired.insert(ptr, months);
            ptr = ptr + 1;
        }
        Self {
            start_date,
            end_date,
            months_when_retired,
        }
    }
}
